package main

import (
	"bufio"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"gopkg.in/gomail.v2"
	"log"
	"os"
	"regexp"
	"sort"
	"strconv"
	"strings"
)

type NginxLog struct {
	Api     string
	Count   int
	Code200 int
	Code302 int
	Code304 int
	Code401 int
	Code404 int
	Code499 int
	Code403 int
	Code500 int
	Time005 int //<0.5
	Time015 int //0.5-1.0
	Time210 int //1.0-2.0
	Time299 int //<2.0
}

type AA []NginxLog

func (s AA) Len() int           { return len(s) }
func (s AA) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
func (s AA) Less(i, j int) bool { return s[i].Count > s[j].Count }

//生成MD5
func md5V(str string) string {
	h := md5.New()
	h.Write([]byte(str))
	return hex.EncodeToString(h.Sum(nil))
}

func GetCodeAndTime(codeStr, timeStr string) (NginxLog) {
	temp := NginxLog{}
	temp.Count = 1;
	code, _ := strconv.Atoi(codeStr)
	switch code {
	case 200:
		temp.Code200 = 1
	case 302:
		temp.Code302 = 1
	case 304:
		temp.Code304 = 1
	case 401:
		temp.Code401 = 1
	case 404:
		temp.Code404 = 1
	case 499, 502:
		temp.Code499 = 1
	case 500:
		temp.Code500 = 1
	}
	time, _ := strconv.ParseFloat(timeStr, 2)
	switch {
	case time > 0 && time < 0.5:
		temp.Time005 = 1
	case time > 0.5 && time < 1:
		temp.Time015 = 1
	case time > 1 && time < 2:
		temp.Time210 = 1
	case time > 2:
		temp.Time299 = 1
	}
	return temp
}

func SendMail(mailTo []string, subject string, body string) error {
	mailConn := map[string]string{
	 "user": "xxx@163.com",
	 "pass": "your password",
	 "host": "smtp.163.com",
	 "port": "465",
	}


	port, _ := strconv.Atoi(mailConn["port"]) //转换端口类型为int
	m := gomail.NewMessage()
	m.SetHeader("From", m.FormatAddress(mailConn["user"], "nginx日志分析"))

	//m.SetHeader("From", mailConn["user"])
	m.SetHeader("To", mailTo...)
	m.SetHeader("Subject", subject)
	m.SetBody("text/html", body)

	d := gomail.NewDialer(mailConn["host"], port, mailConn["user"], mailConn["pass"])
	err := d.DialAndSend(m)
	return err
}

func main() {
	//- - 12.3.41.10 - [22/Apr/2020:00:28:51 +0800] "POST /index/api/getInfo HTTP/1.1" 200 73 "-" "okhttp/3.14.4" "127.0.0.1" 0.028
	r := regexp.MustCompile(`([^ \[]+)\s+([^ \[]+)\s+([\d\.]+)\s+([^ \[]+)\s+\[([^\]]+)\]\s+\"([^"]+)\"\s+(\d{3})\s+(\d+)\s+\"([^"]+)\"\s+\"([^"]+)\"\s+\"([^"]+)\"\s+([\d\.-]+)`)

	//打开文件
	file, err := os.Open("/www/wwwlogs/demo.com.log")
	//file, err := os.Open("e:/log.log")
	if err != nil {
		panic(fmt.Sprintf("open file error:%s", err.Error()))
	}

	defer file.Close()
	scanner := bufio.NewScanner(file)
	urlArray := make(map[string]NginxLog)

	u := regexp.MustCompile(`([^ \[]+)\s+([^ \[]+)`)
	for scanner.Scan() {
		line := scanner.Text()
		ret := r.FindStringSubmatch(line)
		if len(ret) == 0 {
			continue
		}
		//fmt.Printf("str=》%s\n", ret[3])
		tmpUrl := strings.Replace(ret[6], string('"'), "", -1)
		urlRet := u.FindStringSubmatch(tmpUrl)
		if len(urlRet[2]) == 0 {
			continue
		}
		url := urlRet[2]
		//fmt.Print(ret[12])
		urlMd5 := md5V(urlRet[2])
		if _, ok := urlArray[urlMd5]; ok {
			result := GetCodeAndTime(ret[7], ret[12])
			temp := urlArray[urlMd5]
			temp.Count = temp.Count + result.Count
			temp.Code200 = temp.Code200 + result.Code200
			temp.Code302 = temp.Code302 + result.Code302
			temp.Code304 = temp.Code304 + result.Code304
			temp.Code401 = temp.Code401 + result.Code401
			temp.Code404 = temp.Code404 + result.Code404
			temp.Code499 = temp.Code499 + result.Code499
			temp.Code403 = temp.Code403 + result.Code403
			temp.Code500 = temp.Code500 + result.Code500
			temp.Time005 = temp.Time005 + result.Time005
			temp.Time015 = temp.Time015 + result.Time015
			temp.Time210 = temp.Time210 + result.Time210
			temp.Time299 = temp.Time299 + result.Time299
			temp.Api = url
			urlArray[urlMd5] = temp
			//不存在
		} else {
			temp := GetCodeAndTime(ret[7], ret[12])
			temp.Api = url
			urlArray[urlMd5] = temp
		}
	}

	urls:=make(AA,len(urlArray))
	i:=0
	for _,nginxRow := range urlArray{
		urls[i] = nginxRow
		i++
	}
	sort.Stable(urls)

	html := `<table style="table-layout:fixed;">
	<tr>
		<th>访问次数</th>
		<th>Code200</th>
		<th>Code302</th>
		<th>Code304</th>
		<th>Code401</th>
		<th>Code404</th>
		<th>Code499</th>
		<th>Code403</th>
		<th>Code500</th>
		<th>少于0.5s</th>
		<th>0.5~1s</th>
		<th>1~2s</th>
		<th>2s以上</th>
		<th>API</th>
	</tr>
`
	for _, info := range urls {
		html += `<tr><td align="center">` + strconv.Itoa(info.Count) +
			`</td><td align="center">` + strconv.Itoa(info.Code200) +
			`</td><td align="center">` + strconv.Itoa(info.Code302) +
			`</td><td align="center">` + strconv.Itoa(info.Code304) +
			`</td><td align="center">` + strconv.Itoa(info.Code401) +
			`</td><td align="center">` + strconv.Itoa(info.Code404) +
			`</td><td align="center">` + strconv.Itoa(info.Code499) +
			`</td><td align="center">` + strconv.Itoa(info.Code403) +
			`</td><td align="center">` + strconv.Itoa(info.Code500) +
			`</td><td align="center">` + strconv.Itoa(info.Time005) +
			`</td><td align="center">` + strconv.Itoa(info.Time015) +
			`</td><td align="center">` + strconv.Itoa(info.Time210) +
			`</td><td align="center">` + strconv.Itoa(info.Time299) +
			`</td><td>` + string(info.Api) + `</td></tr>`

	}
	html += `</table>`
	//fmt.Print(html)
	//定义收件人
	mailTo := []string{
		"接收的有些地址",
	}
	subject := "nginx日志分析"
	body := html

	err = SendMail(mailTo, subject, body)
	if err != nil {
		log.Println(err)
		fmt.Println("send fail")
		return
	}

	fmt.Println("send successfully")

}
